<!doctype html><html lang=zh-cn dir=ltr>
<head><meta charset=utf-8>
<meta name=viewport content="width=device-width,initial-scale=1"><meta name=description content="SOCKS 是一种网络传输协议，主要用于客户端与外网服务器之间通讯的中间传递，SOCKS 是&amp;quot;SOCKetS&amp;quot;的缩写。 SOCKS5 是 SOCKS4 的升级版，其主要多了鉴定、IPv6、UDP 支持。
SOCKS5 协议可以分为三个部分：
 (1) 协议版本及认证方式 (2) 根据认证方式执行对应的认证 (3) 请求信息  （1）协议版本及认证方式 创建与 SOCKS5 服务器的 TCP 连接后客户端需要先发送请求来协议版本及认证方式，
   VER NMETHODS METHODS     1 1 1-255      VER 是 SOCKS 版本，这里应该是 0x05；
  NMETHODS 是 METHODS 部分的长度；
  METHODS 是客户端支持的认证方式列表，每个方法占 1 字节。当前的定义是：
 0x00 不需要认证 0x01 GSSAPI 0x02 用户名、密码认证 0x03 - 0x7F 由 IANA 分配（保留） 0x80 - 0xFE 为私人方法保留 0xFF 无可接受的方法    服务器回复客户端可用方法："><title>Socks5</title><link rel=canonical href=https://sdttttt.github.io/blog/socks5/>
<link rel=stylesheet href=/scss/style.min.b80bf249ce4a22cf55e8d7340a0b37a2f2c10f54f3a9a49cb94b694a2eb0bbea.css><meta property="og:title" content="Socks5">
<meta property="og:description" content="SOCKS 是一种网络传输协议，主要用于客户端与外网服务器之间通讯的中间传递，SOCKS 是&amp;quot;SOCKetS&amp;quot;的缩写。 SOCKS5 是 SOCKS4 的升级版，其主要多了鉴定、IPv6、UDP 支持。
SOCKS5 协议可以分为三个部分：
 (1) 协议版本及认证方式 (2) 根据认证方式执行对应的认证 (3) 请求信息  （1）协议版本及认证方式 创建与 SOCKS5 服务器的 TCP 连接后客户端需要先发送请求来协议版本及认证方式，
   VER NMETHODS METHODS     1 1 1-255      VER 是 SOCKS 版本，这里应该是 0x05；
  NMETHODS 是 METHODS 部分的长度；
  METHODS 是客户端支持的认证方式列表，每个方法占 1 字节。当前的定义是：
 0x00 不需要认证 0x01 GSSAPI 0x02 用户名、密码认证 0x03 - 0x7F 由 IANA 分配（保留） 0x80 - 0xFE 为私人方法保留 0xFF 无可接受的方法    服务器回复客户端可用方法：">
<meta property="og:url" content="https://sdttttt.github.io/blog/socks5/">
<meta property="og:site_name" content="SDTTTTT">
<meta property="og:type" content="article"><meta property="article:section" content="Blog"><meta property="article:tag" content="network protocol"><meta property="article:modified_time" content="2020-09-24T18:06:32+08:00">
<meta name=twitter:title content="Socks5">
<meta name=twitter:description content="SOCKS 是一种网络传输协议，主要用于客户端与外网服务器之间通讯的中间传递，SOCKS 是&amp;quot;SOCKetS&amp;quot;的缩写。 SOCKS5 是 SOCKS4 的升级版，其主要多了鉴定、IPv6、UDP 支持。
SOCKS5 协议可以分为三个部分：
 (1) 协议版本及认证方式 (2) 根据认证方式执行对应的认证 (3) 请求信息  （1）协议版本及认证方式 创建与 SOCKS5 服务器的 TCP 连接后客户端需要先发送请求来协议版本及认证方式，
   VER NMETHODS METHODS     1 1 1-255      VER 是 SOCKS 版本，这里应该是 0x05；
  NMETHODS 是 METHODS 部分的长度；
  METHODS 是客户端支持的认证方式列表，每个方法占 1 字节。当前的定义是：
 0x00 不需要认证 0x01 GSSAPI 0x02 用户名、密码认证 0x03 - 0x7F 由 IANA 分配（保留） 0x80 - 0xFE 为私人方法保留 0xFF 无可接受的方法    服务器回复客户端可用方法：">
</head><body class=article-page>
<script>(function(){const e="StackColorScheme";localStorage.getItem(e)||localStorage.setItem(e,"auto")})()</script><script>(function(){const t="StackColorScheme",e=localStorage.getItem(t),n=window.matchMedia("(prefers-color-scheme: dark)").matches===!0;e=="dark"||e==="auto"&&n?document.documentElement.dataset.scheme="dark":document.documentElement.dataset.scheme="light"})()</script>
<div class="container main-container flex on-phone--column compact"><aside class="sidebar left-sidebar sticky">
<button class="hamburger hamburger--spin" type=button id=toggle-menu aria-label="Toggle Menu">
<span class=hamburger-box>
<span class=hamburger-inner></span>
</span>
</button>
<header>
<figure class=site-avatar>
<a href=/>
<img src=/img/avatar_hu8e30117ca872857dd9f41f234a693048_441529_300x0_resize_box_3.png width=300 height=300 class=site-logo loading=lazy alt=Avatar>
</a>
</figure><div class=site-meta>
<h1 class=site-name><a href=/>SDTTTTT</a></h1><h2 class=site-description>臭鱼烂虾</h2></div></header><ol class=menu id=main-menu>
<div class=menu-bottom-section>
<li id=dark-mode-toggle><svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-toggle-left" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentcolor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z"/><circle cx="8" cy="12" r="2"/><rect x="2" y="6" width="20" height="12" rx="6"/></svg><svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-toggle-right" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentcolor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z"/><circle cx="16" cy="12" r="2"/><rect x="2" y="6" width="20" height="12" rx="6"/></svg>
<span>Dark Mode</span>
</li></div></ol></aside><main class="main full-width">
<article class=main-article>
<header class=article-header>
<div class=article-details>
<div class=article-title-wrapper>
<h2 class=article-title>
<a href=/blog/socks5/>Socks5</a>
</h2></div><footer class=article-time>
<div><svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-clock" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentcolor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z"/><circle cx="12" cy="12" r="9"/><polyline points="12 7 12 12 15 15"/></svg>
<time class=article-time--reading>
3 minute read
</time>
</div></footer></div></header><section class=article-content>
<p>SOCKS 是一种网络传输协议，主要用于客户端与外网服务器之间通讯的中间传递，SOCKS 是"SOCKetS"的缩写。
SOCKS5 是 SOCKS4 的升级版，其主要多了鉴定、IPv6、UDP 支持。</p><p>SOCKS5 协议可以分为三个部分：</p><ul>
<li>(1) 协议版本及认证方式</li><li>(2) 根据认证方式执行对应的认证</li><li>(3) 请求信息</li></ul><h4 id=1协议版本及认证方式>（1）协议版本及认证方式</h4><p>创建与 SOCKS5 服务器的 TCP 连接后<strong>客户端</strong>需要先发送请求来协议版本及认证方式，</p><div class=table-wrapper><table>
<thead>
<tr>
<th>VER</th><th>NMETHODS</th><th>METHODS</th></tr></thead><tbody>
<tr>
<td>1</td><td>1</td><td>1-255</td></tr></tbody></table></div><ul>
<li>
<p>VER 是 SOCKS 版本，这里应该是 0x05；</p></li><li>
<p>NMETHODS 是 METHODS 部分的长度；</p></li><li>
<p>METHODS 是客户端支持的认证方式列表，每个方法占 1 字节。当前的定义是：</p><ul>
<li>0x00 不需要认证</li><li>0x01 GSSAPI</li><li>0x02 用户名、密码认证</li><li>0x03 - 0x7F 由 IANA 分配（保留）</li><li>0x80 - 0xFE 为私人方法保留</li><li>0xFF 无可接受的方法</li></ul></li></ul><p><strong>服务器</strong>回复客户端可用方法：</p><div class=table-wrapper><table>
<thead>
<tr>
<th>VER</th><th>METHOD</th></tr></thead><tbody>
<tr>
<td>1</td><td>1</td></tr></tbody></table></div><ul>
<li>VER 是 SOCKS 版本，这里应该是 0x05；</li><li>METHOD 是服务端选中的方法。如果返回 0xFF 表示没有一个认证方法被选中，客户端需要关闭连接。</li></ul><p>代码实现：</p><div class=highlight><pre tabindex=0 style=color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-golang data-lang=golang><span style=display:flex><span><span style=color:#66d9ef>type</span> <span style=color:#a6e22e>ProtocolVersion</span> <span style=color:#66d9ef>struct</span> {
</span></span><span style=display:flex><span>    <span style=color:#a6e22e>VER</span> <span style=color:#66d9ef>uint8</span>
</span></span><span style=display:flex><span>    <span style=color:#a6e22e>NMETHODS</span> <span style=color:#66d9ef>uint8</span>
</span></span><span style=display:flex><span>    <span style=color:#a6e22e>METHODS</span> []<span style=color:#66d9ef>uint8</span>
</span></span><span style=display:flex><span>}
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span><span style=color:#66d9ef>func</span> (<span style=color:#a6e22e>s</span> <span style=color:#f92672>*</span><span style=color:#a6e22e>ProtocolVersion</span>) <span style=color:#a6e22e>handshake</span>(<span style=color:#a6e22e>conn</span> <span style=color:#a6e22e>net</span>.<span style=color:#a6e22e>Conn</span>) <span style=color:#66d9ef>error</span> {
</span></span><span style=display:flex><span>    <span style=color:#a6e22e>b</span> <span style=color:#f92672>:=</span> make([]<span style=color:#66d9ef>byte</span>, <span style=color:#ae81ff>255</span>)
</span></span><span style=display:flex><span>    <span style=color:#a6e22e>n</span>, <span style=color:#a6e22e>err</span> <span style=color:#f92672>:=</span> <span style=color:#a6e22e>conn</span>.<span style=color:#a6e22e>Read</span>(<span style=color:#a6e22e>b</span>)
</span></span><span style=display:flex><span>    <span style=color:#66d9ef>if</span> <span style=color:#a6e22e>err</span> <span style=color:#f92672>!=</span> <span style=color:#66d9ef>nil</span> {
</span></span><span style=display:flex><span>        <span style=color:#a6e22e>log</span>.<span style=color:#a6e22e>Println</span>(<span style=color:#a6e22e>err</span>)
</span></span><span style=display:flex><span>        <span style=color:#66d9ef>return</span> <span style=color:#a6e22e>err</span>
</span></span><span style=display:flex><span>    }
</span></span><span style=display:flex><span>    <span style=color:#a6e22e>s</span>.<span style=color:#a6e22e>VER</span> = <span style=color:#a6e22e>b</span>[<span style=color:#ae81ff>0</span>] <span style=color:#75715e>//ReadByte reads and returns a single byte，第一个参数为socks的版本号
</span></span></span><span style=display:flex><span><span style=color:#75715e></span>    <span style=color:#a6e22e>s</span>.<span style=color:#a6e22e>NMETHODS</span> = <span style=color:#a6e22e>b</span>[<span style=color:#ae81ff>1</span>] <span style=color:#75715e>//nmethods是记录methods的长度的。nmethods的长度是1个字节
</span></span></span><span style=display:flex><span><span style=color:#75715e></span>    <span style=color:#66d9ef>if</span> <span style=color:#a6e22e>n</span> <span style=color:#f92672>!=</span> int(<span style=color:#ae81ff>2</span><span style=color:#f92672>+</span><span style=color:#a6e22e>s</span>.<span style=color:#a6e22e>NMETHODS</span>) {
</span></span><span style=display:flex><span>        <span style=color:#66d9ef>return</span> <span style=color:#a6e22e>errors</span>.<span style=color:#a6e22e>New</span>(<span style=color:#e6db74>&#34;协议错误, sNMETHODS不对&#34;</span>)
</span></span><span style=display:flex><span>    }
</span></span><span style=display:flex><span>    <span style=color:#a6e22e>s</span>.<span style=color:#a6e22e>METHODS</span> = <span style=color:#a6e22e>b</span>[<span style=color:#ae81ff>2</span>:<span style=color:#ae81ff>2</span><span style=color:#f92672>+</span><span style=color:#a6e22e>s</span>.<span style=color:#a6e22e>NMETHODS</span>] <span style=color:#75715e>//读取指定长度信息，读取正好len(buf)长度的字节。如果字节数不是指定长度，则返回错误信息和正确的字节数
</span></span></span><span style=display:flex><span><span style=color:#75715e></span>
</span></span><span style=display:flex><span>    <span style=color:#66d9ef>if</span> <span style=color:#a6e22e>s</span>.<span style=color:#a6e22e>VER</span> <span style=color:#f92672>!=</span> <span style=color:#ae81ff>5</span> {
</span></span><span style=display:flex><span>        <span style=color:#66d9ef>return</span> <span style=color:#a6e22e>errors</span>.<span style=color:#a6e22e>New</span>(<span style=color:#e6db74>&#34;该协议不是socks5协议&#34;</span>)
</span></span><span style=display:flex><span>    }
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span>    <span style=color:#75715e>//服务器回应客户端消息:
</span></span></span><span style=display:flex><span><span style=color:#75715e></span>    <span style=color:#75715e>//第一个参数表示版本号为5，即socks5协议，
</span></span></span><span style=display:flex><span><span style=color:#75715e></span>    <span style=color:#75715e>// 第二个参数表示服务端选中的认证方法，0即无需密码访问, 2表示需要用户名和密码进行验证。
</span></span></span><span style=display:flex><span><span style=color:#75715e></span>    <span style=color:#a6e22e>resp</span> <span style=color:#f92672>:=</span>[]<span style=color:#66d9ef>byte</span>{<span style=color:#ae81ff>5</span>, <span style=color:#ae81ff>0</span>}
</span></span><span style=display:flex><span>    <span style=color:#a6e22e>conn</span>.<span style=color:#a6e22e>Write</span>(<span style=color:#a6e22e>resp</span>)
</span></span><span style=display:flex><span>    <span style=color:#66d9ef>return</span> <span style=color:#66d9ef>nil</span>
</span></span><span style=display:flex><span>}
</span></span></code></pre></div><h4 id=2根据认证方式执行对应的认证>（2）根据认证方式执行对应的认证</h4><p>SOCKS5 协议提供 5 种认证方式：</p><ul>
<li>0x00 不需要认证</li><li>0x01 GSSAPI</li><li>0x02 用户名、密码认证</li><li>0x03 - 0x7F 由 IANA 分配（保留）</li><li>0x80 - 0xFE 为私人方法保留</li></ul><p>这里就主要介绍用户名、密码认证。
在客户端、服务端协商使用用户名密码认证后，客户端发出用户名密码：</p><div class=table-wrapper><table>
<thead>
<tr>
<th>鉴定协议版本</th><th>用户名长度</th><th>用户名</th><th>密码长度</th><th>密码</th></tr></thead><tbody>
<tr>
<td>1</td><td>1</td><td>动态</td><td>1</td><td>动态</td></tr></tbody></table></div><p>服务器鉴定后发出如下回应：</p><div class=table-wrapper><table>
<thead>
<tr>
<th>鉴定协议版本</th><th>鉴定状态</th></tr></thead><tbody>
<tr>
<td>1</td><td>1</td></tr></tbody></table></div><p>其中鉴定状态 0x00 表示成功，0x01 表示失败。</p><p>代码实现：</p><div class=highlight><pre tabindex=0 style=color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-golang data-lang=golang><span style=display:flex><span><span style=color:#66d9ef>type</span> <span style=color:#a6e22e>Socks5Auth</span> <span style=color:#66d9ef>struct</span> {
</span></span><span style=display:flex><span>    <span style=color:#a6e22e>VER</span> <span style=color:#66d9ef>uint8</span>
</span></span><span style=display:flex><span>    <span style=color:#a6e22e>ULEN</span> <span style=color:#66d9ef>uint8</span>
</span></span><span style=display:flex><span>    <span style=color:#a6e22e>UNAME</span> <span style=color:#66d9ef>string</span>
</span></span><span style=display:flex><span>    <span style=color:#a6e22e>PLEN</span> <span style=color:#66d9ef>uint8</span>
</span></span><span style=display:flex><span>    <span style=color:#a6e22e>PASSWD</span> <span style=color:#66d9ef>string</span>
</span></span><span style=display:flex><span>}
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span><span style=color:#66d9ef>func</span> (<span style=color:#a6e22e>s</span> <span style=color:#f92672>*</span><span style=color:#a6e22e>Socks5Auth</span>) <span style=color:#a6e22e>authenticate</span>(<span style=color:#a6e22e>conn</span> <span style=color:#a6e22e>net</span>.<span style=color:#a6e22e>Conn</span>) <span style=color:#66d9ef>error</span> {
</span></span><span style=display:flex><span>    <span style=color:#a6e22e>b</span> <span style=color:#f92672>:=</span> make([]<span style=color:#66d9ef>byte</span>, <span style=color:#ae81ff>128</span>)
</span></span><span style=display:flex><span>    <span style=color:#a6e22e>n</span>, <span style=color:#a6e22e>err</span> <span style=color:#f92672>:=</span> <span style=color:#a6e22e>conn</span>.<span style=color:#a6e22e>Read</span>(<span style=color:#a6e22e>b</span>)
</span></span><span style=display:flex><span>    <span style=color:#66d9ef>if</span> <span style=color:#a6e22e>err</span> <span style=color:#f92672>!=</span> <span style=color:#66d9ef>nil</span>{
</span></span><span style=display:flex><span>        <span style=color:#a6e22e>log</span>.<span style=color:#a6e22e>Println</span>(<span style=color:#a6e22e>err</span>)
</span></span><span style=display:flex><span>        <span style=color:#66d9ef>return</span> <span style=color:#a6e22e>err</span>
</span></span><span style=display:flex><span>    }
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span>    <span style=color:#a6e22e>s</span>.<span style=color:#a6e22e>VER</span> = <span style=color:#a6e22e>b</span>[<span style=color:#ae81ff>0</span>]
</span></span><span style=display:flex><span>    <span style=color:#66d9ef>if</span> <span style=color:#a6e22e>s</span>.<span style=color:#a6e22e>VER</span> <span style=color:#f92672>!=</span> <span style=color:#ae81ff>5</span> {
</span></span><span style=display:flex><span>        <span style=color:#66d9ef>return</span> <span style=color:#a6e22e>errors</span>.<span style=color:#a6e22e>New</span>(<span style=color:#e6db74>&#34;该协议不是socks5协议&#34;</span>)
</span></span><span style=display:flex><span>    }
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span>    <span style=color:#a6e22e>s</span>.<span style=color:#a6e22e>ULEN</span> = <span style=color:#a6e22e>b</span>[<span style=color:#ae81ff>1</span>]
</span></span><span style=display:flex><span>    <span style=color:#a6e22e>s</span>.<span style=color:#a6e22e>UNAME</span> = string(<span style=color:#a6e22e>b</span>[<span style=color:#ae81ff>2</span>:<span style=color:#ae81ff>2</span><span style=color:#f92672>+</span><span style=color:#a6e22e>s</span>.<span style=color:#a6e22e>ULEN</span>])
</span></span><span style=display:flex><span>    <span style=color:#a6e22e>s</span>.<span style=color:#a6e22e>PLEN</span> = <span style=color:#a6e22e>b</span>[<span style=color:#ae81ff>2</span><span style=color:#f92672>+</span><span style=color:#a6e22e>s</span>.<span style=color:#a6e22e>ULEN</span><span style=color:#f92672>+</span><span style=color:#ae81ff>1</span>]
</span></span><span style=display:flex><span>    <span style=color:#a6e22e>s</span>.<span style=color:#a6e22e>PASSWD</span> = string(<span style=color:#a6e22e>b</span>[<span style=color:#a6e22e>n</span><span style=color:#f92672>-</span>int(<span style=color:#a6e22e>s</span>.<span style=color:#a6e22e>PLEN</span>):<span style=color:#a6e22e>n</span>])
</span></span><span style=display:flex><span>    <span style=color:#a6e22e>log</span>.<span style=color:#a6e22e>Println</span>(<span style=color:#a6e22e>s</span>.<span style=color:#a6e22e>UNAME</span>, <span style=color:#a6e22e>s</span>.<span style=color:#a6e22e>PASSWD</span>)
</span></span><span style=display:flex><span>    <span style=color:#66d9ef>if</span> <span style=color:#a6e22e>username</span> <span style=color:#f92672>!=</span> <span style=color:#a6e22e>s</span>.<span style=color:#a6e22e>UNAME</span> <span style=color:#f92672>||</span> <span style=color:#a6e22e>passwd</span> <span style=color:#f92672>!=</span> <span style=color:#a6e22e>s</span>.<span style=color:#a6e22e>PASSWD</span> {
</span></span><span style=display:flex><span>        <span style=color:#66d9ef>return</span> <span style=color:#a6e22e>errors</span>.<span style=color:#a6e22e>New</span>(<span style=color:#e6db74>&#34;账号密码错误&#34;</span>)
</span></span><span style=display:flex><span>    }
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span>    <span style=color:#75715e>/**
</span></span></span><span style=display:flex><span><span style=color:#75715e>    回应客户端,响应客户端连接成功
</span></span></span><span style=display:flex><span><span style=color:#75715e>    The server verifies the supplied UNAME and PASSWD, and sends the
</span></span></span><span style=display:flex><span><span style=color:#75715e>    following response:
</span></span></span><span style=display:flex><span><span style=color:#75715e>                            +----+--------+
</span></span></span><span style=display:flex><span><span style=color:#75715e>                            |VER | STATUS |
</span></span></span><span style=display:flex><span><span style=color:#75715e>                            +----+--------+
</span></span></span><span style=display:flex><span><span style=color:#75715e>                            | 1  |   1    |
</span></span></span><span style=display:flex><span><span style=color:#75715e>                            +----+--------+
</span></span></span><span style=display:flex><span><span style=color:#75715e>    A STATUS field of X&#39;00&#39; indicates success. If the server returns a
</span></span></span><span style=display:flex><span><span style=color:#75715e>    `failure&#39; (STATUS value other than X&#39;00&#39;) status, it MUST close the
</span></span></span><span style=display:flex><span><span style=color:#75715e>    connection.
</span></span></span><span style=display:flex><span><span style=color:#75715e>    */</span>
</span></span><span style=display:flex><span>	<span style=color:#a6e22e>resp</span> <span style=color:#f92672>:=</span> []<span style=color:#66d9ef>byte</span>{<span style=color:#ae81ff>0x05</span>, <span style=color:#ae81ff>0x00</span>}
</span></span><span style=display:flex><span>    <span style=color:#a6e22e>conn</span>.<span style=color:#a6e22e>Write</span>(<span style=color:#a6e22e>resp</span>)
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span>    <span style=color:#66d9ef>return</span> <span style=color:#66d9ef>nil</span>
</span></span><span style=display:flex><span>}
</span></span></code></pre></div><p>但其实，现在大家都习惯自己采用加密流的方式进行加密，很少采用用户名密码的形式进行加密，后面章节会介绍一种对 SOCKS 的混淆加密方式。</p><h4 id=3请求信息>（3）请求信息</h4><p>认证结束后客户端就可以发送请求信息。如果认证方法有特殊封装要求，请求必须按照方法所定义的方式进行封装解密，其原始格式如下：</p><div class=table-wrapper><table>
<thead>
<tr>
<th>VER</th><th>CMD</th><th>RSV</th><th>ATYP</th><th>DST.ADDR</th><th>DST.PORT</th></tr></thead><tbody>
<tr>
<td>1</td><td>1</td><td>0x00</td><td>1</td><td>动态</td><td>2</td></tr></tbody></table></div><ul>
<li>VER 是 SOCKS 版本，这里应该是 0x05；</li><li>CMD 是 SOCK 的命令码
<ul>
<li>0x01 表示 CONNECT 请求</li><li>0x02 表示 BIND 请求</li><li>0x03 表示 UDP 转发</li></ul></li><li>RSV 0x00，保留</li><li>ATYP DST.ADDR 类型</li><li>DST.ADDR 目的地址
<ul>
<li>0x01 IPv4 地址，DST.ADDR 部分 4 字节长度</li><li>0x03 域名，DST.ADDR 部分第一个字节为域名长度，DST.ADDR 剩余的内容为域名，没有\0 结尾。</li><li>0x04 IPv6 地址，16 个字节长度。</li></ul></li><li>DST.PORT 网络字节序表示的目的端口</li></ul><p>代码实现：</p><div class=highlight><pre tabindex=0 style=color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-golang data-lang=golang><span style=display:flex><span><span style=color:#66d9ef>type</span> <span style=color:#a6e22e>Socks5Resolution</span> <span style=color:#66d9ef>struct</span> {
</span></span><span style=display:flex><span>    <span style=color:#a6e22e>VER</span> <span style=color:#66d9ef>uint8</span>
</span></span><span style=display:flex><span>    <span style=color:#a6e22e>CMD</span> <span style=color:#66d9ef>uint8</span>
</span></span><span style=display:flex><span>    <span style=color:#a6e22e>RSV</span> <span style=color:#66d9ef>uint8</span>
</span></span><span style=display:flex><span>    <span style=color:#a6e22e>ATYP</span> <span style=color:#66d9ef>uint8</span>
</span></span><span style=display:flex><span>    <span style=color:#a6e22e>DSTADDR</span> []<span style=color:#66d9ef>byte</span>
</span></span><span style=display:flex><span>    <span style=color:#a6e22e>DSTPORT</span> <span style=color:#66d9ef>uint16</span>
</span></span><span style=display:flex><span>    <span style=color:#a6e22e>DSTDOMAIN</span> <span style=color:#66d9ef>string</span>
</span></span><span style=display:flex><span>    <span style=color:#a6e22e>RAWADDR</span> <span style=color:#f92672>*</span><span style=color:#a6e22e>net</span>.<span style=color:#a6e22e>TCPAddr</span>
</span></span><span style=display:flex><span>}
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span><span style=color:#66d9ef>func</span> (<span style=color:#a6e22e>s</span> <span style=color:#f92672>*</span><span style=color:#a6e22e>Socks5Resolution</span>) <span style=color:#a6e22e>lstRequest</span>(<span style=color:#a6e22e>conn</span> <span style=color:#a6e22e>net</span>.<span style=color:#a6e22e>Conn</span>) <span style=color:#66d9ef>error</span> {
</span></span><span style=display:flex><span>    <span style=color:#a6e22e>b</span> <span style=color:#f92672>:=</span> make([]<span style=color:#66d9ef>byte</span>, <span style=color:#ae81ff>128</span>)
</span></span><span style=display:flex><span>    <span style=color:#a6e22e>n</span>, <span style=color:#a6e22e>err</span> <span style=color:#f92672>:=</span> <span style=color:#a6e22e>conn</span>.<span style=color:#a6e22e>Read</span>(<span style=color:#a6e22e>b</span>)
</span></span><span style=display:flex><span>    <span style=color:#66d9ef>if</span> <span style=color:#a6e22e>err</span> <span style=color:#f92672>!=</span> <span style=color:#66d9ef>nil</span> <span style=color:#f92672>||</span> <span style=color:#a6e22e>n</span> &lt; <span style=color:#ae81ff>7</span> {
</span></span><span style=display:flex><span>        <span style=color:#a6e22e>log</span>.<span style=color:#a6e22e>Println</span>(<span style=color:#a6e22e>err</span>)
</span></span><span style=display:flex><span>        <span style=color:#66d9ef>return</span> <span style=color:#a6e22e>errors</span>.<span style=color:#a6e22e>New</span>(<span style=color:#e6db74>&#34;请求协议错误&#34;</span>)
</span></span><span style=display:flex><span>    }
</span></span><span style=display:flex><span>    <span style=color:#a6e22e>s</span>.<span style=color:#a6e22e>VER</span> = <span style=color:#a6e22e>b</span>[<span style=color:#ae81ff>0</span>]
</span></span><span style=display:flex><span>    <span style=color:#66d9ef>if</span> <span style=color:#a6e22e>s</span>.<span style=color:#a6e22e>VER</span> <span style=color:#f92672>!=</span> <span style=color:#ae81ff>5</span> {
</span></span><span style=display:flex><span>        <span style=color:#66d9ef>return</span> <span style=color:#a6e22e>errors</span>.<span style=color:#a6e22e>New</span>(<span style=color:#e6db74>&#34;该协议不是socks5协议&#34;</span>)
</span></span><span style=display:flex><span>    }
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span>    <span style=color:#a6e22e>s</span>.<span style=color:#a6e22e>CMD</span> = <span style=color:#a6e22e>b</span>[<span style=color:#ae81ff>1</span>]
</span></span><span style=display:flex><span>    <span style=color:#66d9ef>if</span> <span style=color:#a6e22e>s</span>.<span style=color:#a6e22e>CMD</span> <span style=color:#f92672>!=</span> <span style=color:#ae81ff>1</span> {
</span></span><span style=display:flex><span>        <span style=color:#66d9ef>return</span> <span style=color:#a6e22e>errors</span>.<span style=color:#a6e22e>New</span>(<span style=color:#e6db74>&#34;客户端请求类型不为代理连接, 其他功能暂时不支持.&#34;</span>)
</span></span><span style=display:flex><span>    }
</span></span><span style=display:flex><span>    <span style=color:#a6e22e>s</span>.<span style=color:#a6e22e>RSV</span> = <span style=color:#a6e22e>b</span>[<span style=color:#ae81ff>2</span>] <span style=color:#75715e>//RSV保留字端，值长度为1个字节
</span></span></span><span style=display:flex><span><span style=color:#75715e></span>
</span></span><span style=display:flex><span>    <span style=color:#a6e22e>s</span>.<span style=color:#a6e22e>ATYP</span> = <span style=color:#a6e22e>b</span>[<span style=color:#ae81ff>3</span>]
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span>    <span style=color:#66d9ef>switch</span> <span style=color:#a6e22e>s</span>.<span style=color:#a6e22e>ATYP</span> {
</span></span><span style=display:flex><span>    <span style=color:#66d9ef>case</span> <span style=color:#ae81ff>1</span>:
</span></span><span style=display:flex><span>        <span style=color:#75715e>//	IP V4 address: X&#39;01&#39;
</span></span></span><span style=display:flex><span><span style=color:#75715e></span>        <span style=color:#a6e22e>s</span>.<span style=color:#a6e22e>DSTADDR</span> = <span style=color:#a6e22e>b</span>[<span style=color:#ae81ff>4</span> : <span style=color:#ae81ff>4</span><span style=color:#f92672>+</span><span style=color:#a6e22e>net</span>.<span style=color:#a6e22e>IPv4len</span>]
</span></span><span style=display:flex><span>    <span style=color:#66d9ef>case</span> <span style=color:#ae81ff>3</span>:
</span></span><span style=display:flex><span>        <span style=color:#75715e>//	DOMAINNAME: X&#39;03&#39;
</span></span></span><span style=display:flex><span><span style=color:#75715e></span>        <span style=color:#a6e22e>s</span>.<span style=color:#a6e22e>DSTDOMAIN</span> = string(<span style=color:#a6e22e>b</span>[<span style=color:#ae81ff>5</span>:<span style=color:#a6e22e>n</span><span style=color:#f92672>-</span><span style=color:#ae81ff>2</span>])
</span></span><span style=display:flex><span>        <span style=color:#a6e22e>ipAddr</span>, <span style=color:#a6e22e>err</span> <span style=color:#f92672>:=</span> <span style=color:#a6e22e>net</span>.<span style=color:#a6e22e>ResolveIPAddr</span>(<span style=color:#e6db74>&#34;ip&#34;</span>, <span style=color:#a6e22e>s</span>.<span style=color:#a6e22e>DSTDOMAIN</span>)
</span></span><span style=display:flex><span>		<span style=color:#66d9ef>if</span> <span style=color:#a6e22e>err</span> <span style=color:#f92672>!=</span> <span style=color:#66d9ef>nil</span> {
</span></span><span style=display:flex><span>			<span style=color:#66d9ef>return</span> <span style=color:#a6e22e>err</span>
</span></span><span style=display:flex><span>		}
</span></span><span style=display:flex><span>        <span style=color:#a6e22e>s</span>.<span style=color:#a6e22e>DSTADDR</span> = <span style=color:#a6e22e>ipAddr</span>.<span style=color:#a6e22e>IP</span>
</span></span><span style=display:flex><span>    <span style=color:#66d9ef>case</span> <span style=color:#ae81ff>4</span>:
</span></span><span style=display:flex><span>        <span style=color:#75715e>//	IP V6 address: X&#39;04&#39;
</span></span></span><span style=display:flex><span><span style=color:#75715e></span>        <span style=color:#a6e22e>s</span>.<span style=color:#a6e22e>DSTADDR</span> = <span style=color:#a6e22e>b</span>[<span style=color:#ae81ff>4</span> : <span style=color:#ae81ff>4</span><span style=color:#f92672>+</span><span style=color:#a6e22e>net</span>.<span style=color:#a6e22e>IPv6len</span>]
</span></span><span style=display:flex><span>	<span style=color:#66d9ef>default</span>:
</span></span><span style=display:flex><span>		<span style=color:#66d9ef>return</span> <span style=color:#a6e22e>errors</span>.<span style=color:#a6e22e>New</span>(<span style=color:#e6db74>&#34;IP地址错误&#34;</span>)
</span></span><span style=display:flex><span>    }
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span>    <span style=color:#a6e22e>s</span>.<span style=color:#a6e22e>DSTPORT</span> = <span style=color:#a6e22e>binary</span>.<span style=color:#a6e22e>BigEndian</span>.<span style=color:#a6e22e>Uint16</span>(<span style=color:#a6e22e>b</span>[<span style=color:#a6e22e>n</span><span style=color:#f92672>-</span><span style=color:#ae81ff>2</span>:<span style=color:#a6e22e>n</span>])
</span></span><span style=display:flex><span>    <span style=color:#75715e>// DSTADDR全部换成IP地址，可以防止DNS污染和封杀
</span></span></span><span style=display:flex><span><span style=color:#75715e></span>    <span style=color:#a6e22e>s</span>.<span style=color:#a6e22e>RAWADDR</span> = <span style=color:#f92672>&amp;</span><span style=color:#a6e22e>net</span>.<span style=color:#a6e22e>TCPAddr</span>{
</span></span><span style=display:flex><span>		<span style=color:#a6e22e>IP</span>:   <span style=color:#a6e22e>s</span>.<span style=color:#a6e22e>DSTADDR</span>,
</span></span><span style=display:flex><span>		<span style=color:#a6e22e>Port</span>: int(<span style=color:#a6e22e>s</span>.<span style=color:#a6e22e>DSTPORT</span>),
</span></span><span style=display:flex><span>    }
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span>    <span style=color:#75715e>/**
</span></span></span><span style=display:flex><span><span style=color:#75715e>    回应客户端,响应客户端连接成功
</span></span></span><span style=display:flex><span><span style=color:#75715e>        +----+-----+-------+------+----------+----------+
</span></span></span><span style=display:flex><span><span style=color:#75715e>        |VER | REP |  RSV  | ATYP | BND.ADDR | BND.PORT |
</span></span></span><span style=display:flex><span><span style=color:#75715e>        +----+-----+-------+------+----------+----------+
</span></span></span><span style=display:flex><span><span style=color:#75715e>        | 1  |  1  | X&#39;00&#39; |  1   | Variable |    2     |
</span></span></span><span style=display:flex><span><span style=color:#75715e>        +----+-----+-------+------+----------+----------+
</span></span></span><span style=display:flex><span><span style=color:#75715e>    */</span>
</span></span><span style=display:flex><span>	<span style=color:#a6e22e>resp</span> <span style=color:#f92672>:=</span> []<span style=color:#66d9ef>byte</span>{<span style=color:#ae81ff>0x05</span>, <span style=color:#ae81ff>0x00</span>, <span style=color:#ae81ff>0x00</span>, <span style=color:#ae81ff>0x01</span>, <span style=color:#ae81ff>0x00</span>, <span style=color:#ae81ff>0x00</span>, <span style=color:#ae81ff>0x00</span>, <span style=color:#ae81ff>0x00</span>, <span style=color:#ae81ff>0x00</span>, <span style=color:#ae81ff>0x00</span>}
</span></span><span style=display:flex><span>    <span style=color:#a6e22e>conn</span>.<span style=color:#a6e22e>Write</span>(<span style=color:#a6e22e>resp</span>)
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span>    <span style=color:#66d9ef>return</span> <span style=color:#66d9ef>nil</span>
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span>}
</span></span></code></pre></div><h4 id=4最后将信息进行转发即可>（4）最后将信息进行转发即可</h4><p>代码实现:</p><div class=highlight><pre tabindex=0 style=color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-golang data-lang=golang><span style=display:flex><span>    <span style=color:#a6e22e>wg</span> <span style=color:#f92672>:=</span> new(<span style=color:#a6e22e>sync</span>.<span style=color:#a6e22e>WaitGroup</span>)
</span></span><span style=display:flex><span>    <span style=color:#a6e22e>wg</span>.<span style=color:#a6e22e>Add</span>(<span style=color:#ae81ff>2</span>)
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span>    <span style=color:#66d9ef>go</span> <span style=color:#66d9ef>func</span>() {
</span></span><span style=display:flex><span>		<span style=color:#66d9ef>defer</span> <span style=color:#a6e22e>wg</span>.<span style=color:#a6e22e>Done</span>()
</span></span><span style=display:flex><span>		<span style=color:#66d9ef>defer</span> <span style=color:#a6e22e>dstServer</span>.<span style=color:#a6e22e>Close</span>()
</span></span><span style=display:flex><span>        <span style=color:#a6e22e>io</span>.<span style=color:#a6e22e>Copy</span>(<span style=color:#a6e22e>dstServer</span>, <span style=color:#a6e22e>client</span>)
</span></span><span style=display:flex><span>    }()
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span>    <span style=color:#66d9ef>go</span> <span style=color:#66d9ef>func</span>() {
</span></span><span style=display:flex><span>		<span style=color:#66d9ef>defer</span> <span style=color:#a6e22e>wg</span>.<span style=color:#a6e22e>Done</span>()
</span></span><span style=display:flex><span>        <span style=color:#66d9ef>defer</span> <span style=color:#a6e22e>client</span>.<span style=color:#a6e22e>Close</span>()
</span></span><span style=display:flex><span>        <span style=color:#a6e22e>io</span>.<span style=color:#a6e22e>Copy</span>(<span style=color:#a6e22e>client</span>, <span style=color:#a6e22e>dstServer</span>)
</span></span><span style=display:flex><span>    }()
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span>    <span style=color:#a6e22e>wg</span>.<span style=color:#a6e22e>Wait</span>()
</span></span></code></pre></div></section><footer class=article-footer>
<section class=article-tags>
<a href=/tags/network-protocol/>network protocol</a>
</section><section class=article-lastmod><svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-clock" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentcolor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z"/><circle cx="12" cy="12" r="9"/><polyline points="12 7 12 12 15 15"/></svg>
<span>
Last updated on Sep 24, 2020 18:06 CST
</span>
</section></footer></article><div id=gitalk-container></div><link rel=stylesheet href=https://cdn.jsdelivr.net/npm/gitalk@1.7.2/dist/gitalk.css>
<script src=https://cdn.jsdelivr.net/npm/gitalk@1.7.2/dist/gitalk.min.js></script>
<script src=https://cdn.jsdelivr.net/npm/blueimp-md5@2.18.0/js/md5.min.js></script>
<script>const gitalk=new Gitalk({clientID:"97eb9ce8ac126f0c7833",clientSecret:"5da440441b500b0b016928640712a1b1a03a5f8f",repo:"sdttttt/sdttttt.github.io",owner:"sdttttt",admin:["sdttttt"],distractionFreeMode:!1,id:md5(location.pathname)});(function(){if(["localhost","127.0.0.1"].indexOf(window.location.hostname)!=-1){document.getElementById("gitalk-container").innerHTML="Gitalk comments not available by default when the website is previewed locally.";return}gitalk.render("gitalk-container")})()</script>
<footer class=site-footer>
<section class=copyright>
&copy;
2022 SDTTTTT
</section><section class=powerby>
Built with <a href=https://gohugo.io/ target=_blank rel=noopener>Hugo</a> <br>
Theme <b><a href=https://github.com/CaiJimmy/hugo-theme-stack target=_blank rel=noopener data-version=3.10.0>Stack</a></b> designed by <a href=https://jimmycai.com target=_blank rel=noopener>Jimmy</a>
</section></footer><div class=pswp tabindex=-1 role=dialog aria-hidden=true>
<div class=pswp__bg></div><div class=pswp__scroll-wrap>
<div class=pswp__container>
<div class=pswp__item></div><div class=pswp__item></div><div class=pswp__item></div></div><div class="pswp__ui pswp__ui--hidden">
<div class=pswp__top-bar>
<div class=pswp__counter></div><button class="pswp__button pswp__button--close" title="Close (Esc)"></button>
<button class="pswp__button pswp__button--share" title=Share></button>
<button class="pswp__button pswp__button--fs" title="Toggle fullscreen"></button>
<button class="pswp__button pswp__button--zoom" title="Zoom in/out"></button>
<div class=pswp__preloader>
<div class=pswp__preloader__icn>
<div class=pswp__preloader__cut>
<div class=pswp__preloader__donut></div></div></div></div></div><div class="pswp__share-modal pswp__share-modal--hidden pswp__single-tap">
<div class=pswp__share-tooltip></div></div><button class="pswp__button pswp__button--arrow--left" title="Previous (arrow left)">
</button>
<button class="pswp__button pswp__button--arrow--right" title="Next (arrow right)">
</button>
<div class=pswp__caption>
<div class=pswp__caption__center></div></div></div></div></div><script src=https://cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/photoswipe.min.js integrity="sha256-ePwmChbbvXbsO02lbM3HoHbSHTHFAeChekF1xKJdleo=" crossorigin=anonymous defer></script><script src=https://cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/photoswipe-ui-default.min.js integrity="sha256-UKkzOn/w1mBxRmLLGrSeyB4e1xbrp4xylgAWb3M42pU=" crossorigin=anonymous defer></script><link rel=stylesheet href=https://cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/default-skin/default-skin.css integrity="sha256-c0uckgykQ9v5k+IqViZOZKc47Jn7KQil4/MP3ySA3F8=" crossorigin=anonymous><link rel=stylesheet href=https://cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/photoswipe.css integrity="sha256-SBLU4vv6CA6lHsZ1XyTdhyjJxCjPif/TRkjnsyGAGnE=" crossorigin=anonymous>
</main></div><script src=https://cdn.jsdelivr.net/npm/node-vibrant@3.1.5/dist/vibrant.min.js integrity="sha256-5NovOZc4iwiAWTYIFiIM7DxKUXKWvpVEuMEPLzcm5/g=" crossorigin=anonymous></script><script type=text/javascript src=/ts/main.js defer></script>
<script>(function(){const e=document.createElement("link");e.href="https://fonts.googleapis.com/css2?family=Lato:wght@300;400;700&display=swap",e.type="text/css",e.rel="stylesheet",document.head.appendChild(e)})()</script>
</body></html>